from classDef import ErrorCode,priority_map
from enum import IntEnum
from treeKernel import fnode
from termcolor import colored
class P(IntEnum):
    OWNER_ONLY  =  0b10000000
    OWNER       =  0b11110000
    ADMIN       =  0b01110000
    WRITE       =  0b00110000
    READ        =  0b00010000
    AGENT_OWNER =  0b00001111
    AGENT_ADMIN =  0b00000111
    AGENT_WRITE =  0b00000011
    AGENT_READ  =  0b00000001
    NONE        =  0b00000000

class permissionMat:
    """
    用于根据用户在"文档(doc)"或"空间(space)"拥有的权限，
    判断某项具体操作是否被允许执行的类。

    以您的表格为基础：
    operation\permission | doc O | doc A | doc W | doc R | space O | space A | space W | space R | root O
    -----------------------------------------------------------------------------------------
    createDoc            |   ✗   |   ✗   |   ✗   |   ✗   |   ✓    |   ✓    |   ✓    |   ✗    |   ✗
    createSpace          |   ✗   |   ✗   |   ✗   |   ✗   |   ✗    |   ✗    |   ✗    |   ✗    |   ✓
    renameDoc            |   ✓   |   ✓   |   ✓   |   ✗   |   ✓    |   ✓    |   ✓    |   ✗    |   ✗
    renameSpace          |   ✗   |   ✗   |   ✗   |   ✗   |   ✓    |   ✓    |   ✗    |   ✗    |   ✗
    deleteDoc            |   ✓   |   ✓   |   ✓   |   ✗   |   ✓    |   ✓    |   ✓    |   ✗    |   ✗
    deleteSpace          |   ✗   |   ✗   |   ✗   |   ✗   |   ✓    |   ✗    |   ✗    |   ✗    |   ✗
    writeDoc             |   ✓   |   ✓   |   ✓   |   ✗   |   ✓    |   ✓    |   ✓    |   ✗    |   ✗
    movefromDoc          |   ✓   |   ✓   |   ✓   |   ✗   |   ✓    |   ✓    |   ✓    |   ✗    |   ✗
    movetoSpace          |   ✗   |   ✗   |   ✗   |   ✗   |   ✓    |   ✓    |   ✓    |   ✗    |   ✗
    copyfromDoc          |   ✓   |   ✓   |   ✓   |   ✓   |   ✓    |   ✓    |   ✓    |   ✓    |   ✗
    copytoSpace          |   ✗   |   ✗   |   ✗   |   ✗   |   ✓    |   ✓    |   ✓    |   ✗    |   ✗
    readDoc              |   ✓   |   ✓   |   ✓   |   ✓   |   ✓    |   ✓    |   ✓    |   ✓    |   ✗
    readSpace            |   ✗   |   ✗   |   ✗   |   ✗   |   ✓    |   ✓    |   ✓    |   ✓    |   ✗
    share space O/A/W/R  |   ✗   |   ✗   |   ✗   |   ✗   |   ✓    |   ✓    |   ✗    |   ✗    |   ✗
    share doc O/A/W/R    |   ✓   |   ✓   |   ✗   |   ✗   |   ✓    |   ✓    |   ✗    |   ✗    |   ✗

    permissionMat的逻辑是，你有什么操作，当前是什么类型的用户权限，我告诉你能不能做。
    setContext的逻辑是，如果至少有用户读，那么设置agent读.至于agent到底满足什么条件读，与setContext无关。
    permissionMat不能用于权限的赋值，只能用于checkP.
    """

    def __init__(self,err:ErrorCode):
        self.err=err
        # 以字典嵌套字典的形式存储，值为 bool（1 代表允许操作，0 代表不允许）
        # 也可以直接使用 0/1 或其它形式
        self.matrix = {
            "createdoc":   {"docO": 0,  "docA": 0, "docW": 0,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 0, "rootO": 0},
            "createspace": {"docO": 0,  "docA": 0, "docW": 0,  "docR": 0, "spaceO": 0,  "spaceA": 0,  "spaceW": 0,  "spaceR": 0, "rootO": 1},
            "renamedoc":   {"docO": 1,  "docA": 1, "docW": 1,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 0, "rootO": 0},
            "renamespace": {"docO": 0,  "docA": 0, "docW": 0,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 0,  "spaceR": 0, "rootO": 0},
            "deletedoc":   {"docO": 1,  "docA": 1, "docW": 1,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 0, "rootO": 0},
            "deletespace": {"docO": 0,  "docA": 0, "docW": 0,  "docR": 0, "spaceO": 1,  "spaceA": 0,  "spaceW": 0,  "spaceR": 0, "rootO": 0},
            "movefromdoc": {"docO": 1,  "docA": 1, "docW": 1,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 0, "rootO": 0},
            "movetospace": {"docO": 0,  "docA": 0, "docW": 0,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 0, "rootO": 0},
            "copyfromdoc": {"docO": 1,  "docA": 1, "docW": 1,  "docR": 1, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 1, "rootO": 0},
            "copytospace": {"docO": 0,  "docA": 0, "docW": 0,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 0, "rootO": 0},
            "readdoc":     {"docO": 1,  "docA": 1, "docW": 1,  "docR": 1, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 1, "rootO": 0},
            "readspace":   {"docO": 1,  "docA": 1, "docW": 1,  "docR": 1, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 1, "rootO": 0},
            "sharedoc":    {"docO": 1,  "docA": 1, "docW": 0,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 0,  "spaceR": 0, "rootO": 0},
            "sharespace":  {"docO": 0,  "docA": 0, "docW": 0,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 0,  "spaceR": 0, "rootO": 0},
            "writedoc":    {"docO": 1,  "docA": 1, "docW": 1,  "docR": 0, "spaceO": 1,  "spaceA": 1,  "spaceW": 1,  "spaceR": 0, "rootO": 0},
        }
        # create的发起节点和作用对象节点类型一高一低
        # 所有oper后面加的onType如果是space(作用对象节点),那么无从定义发起对space操作的doc节点，所以都为0.
        #   （除了readSpace，含义是readSpaceName(DPsearch用)，即就算对空间没有OAWR中任何权限，
        #    只要对空间中一个文件有OAWR的一种，就可以读空间名（readSpaceName不代表拥有了spaceR的权限）
        #    所以作用对象先是space,操作时readSpace,如果失败，再尝试搜素空间中是否有任何文件它可以O/A/W/R，如果有就也仍可以readSpace.这样可以免去agentPermEscalate.
        # 但是，如果oper后面加的onType是doc(作用对象节点),而它的作用对象可以是space也可以是doc。
        # 一般会传来doc来判断权限，如果没有权限也可以这个doc的space作为发起节点，再试一次作用于此doc对象。
    def greater(self,permB:int,permS:int) -> bool:
        """
        返回 permB 是否完全包含 permS。
        """
        return permB & permS == permS
    def pm(self,user: str,node:fnode,oper: str,by: str)->None:
        """
        ## 返回None，只要不报错就有权限
        检查在给定 operation 和 permission 的条件下，是否被允许执行操作
        :param oper+doc/space:  ["createdoc","createspace","renamedoc", "renamespace","deletedoc","deletespace","writedoc","movedoc","copydoc", "readdoc","readspace","sharespace","sharedoc"]
        :param doc/space+perm: ["docO","docA","docW","docR","spaceO","spaceA","spaceW","spaceR"]
        :return: bool, 1 代表允许执行
        """
        nodeType=node.node_type
        permission=node.permission.get(user,P.NONE)
        # print(colored(f"pm user {user} node {node.id} {node.content} oper {oper} by {by}","light_cyan"))
        #process nodeType
        if node.id==1:
            nodeType="root"
        if nodeType not in ["doc","space","root"]:
            if (nodeType in priority_map.keys() or nodeType.strip()=="") and oper=="read":
                nodeType="doc"
            else:
                raise ValueError(self.err.TYPE_NOT_MATCH(f"nodeType:{nodeType} not within [doc,space,root,mdTypes]"))
        #process onType
        onType=nodeType
        if oper=="create":
            if nodeType=="root":
                onType="space"
            elif nodeType=="space":
                onType="doc"
            else:
                raise ValueError(self.err.TYPE_NOT_MATCH(f"doc node can not create anything"))
        #process operstr
        operstr=oper+onType
        #process permChar
        permChar=""
#         if (permission>>4==0 and by=="user") or (permission&0b1111==0 and by=="agent"):
#             if nodeType=="space":#only readSpace actually
#                 #docHelpSpace
#                 for child in node.children:
#                     if self.greater(child.permission.get(user,P.NONE),P.READ if by=="user" else P.AGENT_READ):
#                         print(colored(f"[{child.node_type} {child.id}({child.permission.get(user,P.NONE):08b}>{P.READ if by=='user' else P.AGENT_READ})Help {node.node_type} {node.id}({permission:08b} in {operstr})]","light_green"))
# #                         print(colored(f"""[docHelpSpace]{nodeType} {node.id} {node.content} perm first or last 4 empty:{permission:08b}.
# # [docHelpSpace]{user}'s {child.node_type} {child.id} {node.content} with perm {child.permission.get(user,P.NONE):08b} greater than {(P.READ if by=='user' else P.AGENT_READ):08b} helping readspaceName""",'light_cyan'))
#                         return
#             if nodeType=="doc":#spaceHelpDoc
#                 if not node.parent:
#                     raise ValueError(self.err.NODE_NOT_FOUND(f"node parent from spaceHelpDoc not found.node {node.node_type} {node.id} {node.content}",-1))
#                 else:
#                     self.pm(user,node.parent,oper,by)
#                     print(colored(f"[{node.parent.node_type} {node.parent.id}({node.parent.permission.get(user,P.NONE):08b}>{by}{oper})Help {node.node_type} {node.id}({permission:08b} in {operstr})]","light_green"))
# #                     print(colored(f"""[spaceHelpDoc]{nodeType} {node.id} {node.content} perm first or last 4 empty:{permission:08b}.
# # [spaceHelpDoc]{user}'s node parent {node.parent.node_type} {node.parent.id} {node.parent.content} with perm {node.parent.permission.get(user,P.NONE):08b} helping Doc {operstr}""","light_green"))
#                     return 
        if by=="user":
            if self.greater(permission,P.OWNER):
                permChar="O"#owner
            elif self.greater(permission,P.ADMIN):
                permChar="A"#admin
            elif self.greater(permission,P.WRITE):
                permChar="W"#write
            elif self.greater(permission,P.READ):
                permChar="R"#read
            else:
                raise ValueError(self.err.NO_PERMISSION(f"by user,irregular permission:{permission:08b} can not cover either of [{P.OWNER:08b},{P.ADMIN:08b},{P.WRITE:08b},{P.READ:08b}]"))
        elif by=="agent":
            if self.greater(permission,P.AGENT_OWNER):
                permChar="O"#owner
            elif self.greater(permission,P.AGENT_ADMIN):
                permChar="A"#admin
            elif self.greater(permission,P.AGENT_WRITE):
                permChar="W"#write
            elif self.greater(permission,P.AGENT_READ):
                permChar="R"#read
            else:
                raise ValueError(self.err.NO_PERMISSION(f"by agent,irregular permission:{permission:08b} can not cover either of [{P.OWNER:08b},{P.AGENT_ADMIN:08b},{P.AGENT_WRITE:08b},{P.AGENT_READ:08b}]"))
        else:
            raise ValueError(self.err.TYPE_NOT_MATCH(f"by:{by} not within [user,agent]"))
        #process permstr
        permstr=nodeType+permChar
        if operstr=="readroot":
            return
        if operstr not in self.matrix:
            raise ValueError(self.err.TYPE_NOT_MATCH(f"operation:{operstr} not within {self.matrix.keys()}"))
        if permstr not in self.matrix[operstr]:
            raise ValueError(self.err.TYPE_NOT_MATCH(f"permission:{permstr} not within {self.matrix[operstr].keys()}"))
        if not self.matrix[operstr][permstr]:
            if onType=="doc":
                permstr="space"+permChar
                print(colored(f"After mat, user's space permission helping doc. pm {user} id:{node.id} oper:{oper} by:{by} perm:{permstr} operstr:{operstr} permission:{permission:08b}",'light_cyan'))
                if self.matrix[operstr][permstr]:
                    print("space helped doc pass check")
                    return
            elif operstr=="readspace":
                for child in node.children:
                    if self.greater(child.permission.get(user,P.NONE),P.READ if by=="user" else P.AGENT_READ):
                        print(colored(f"After mat, user's doc permission helping space. pm {user} id:{node.id} oper:{oper} by:{by} perm:{permstr} operstr:{operstr} permission:{permission:08b}",'light_cyan'))
                        return
            raise ValueError(self.err.NO_PERMISSION(f"{by} with permission {permission:08b} {permstr} can not do {operstr}, but can do {[oper for oper in self.matrix if self.matrix[oper][permstr]]}"))
        return
        # print(colored(f"pm -user {user} -node {node.id} -oper {oper} -by {by} -perm {permstr} -operstr {operstr} -permission {permission:08b}",'light_cyan'))